package se.webbzon.oschi01.nbt;

import java.util.Iterator;

import se.webbzon.oschi01.nbt.TagEvent.Action;

class TagRing<T> implements TagSibling, TagGeneration<T> {
	
	private final Tag<?> parent;
	private boolean silent = false;
	private int siblings = 0;
	private TagSibling sibling = this;
	private TagSibling last = this;
	
	TagRing(Tag<?> parent) {
		this.parent = parent;
	}
	
	public boolean isSilent() {
		return silent;
	}
	
	public void setSilent(boolean silent) {
		this.silent = silent;
	}

	@Override public boolean hasParent() {
		return (parent != null);
	}

	@Override public Tag<?> getParent() {
		return parent;
	}

	@Override public TagSibling getSibling() {
		return sibling;
	}

	@Override public void setSibling(TagSibling sibling) {
		this.sibling = sibling;
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override public TagGeneration<Tag<?>> getGeneration() {
		TagGeneration generation = this;
		return generation;
	}
	
	@Override public boolean isEmpty() {
		return sibling == this;
	}
	
	@Override public int siblings() {
		return siblings;
	}
	
	@SuppressWarnings("unchecked")
	@Override public Tag<T> getSibling(int index) {
		if (index >= siblings) {
			throw new IndexOutOfBoundsException();
		} else {
			TagSibling s = this.sibling;
			for (int i = 0; i < index; i++) {
				s = s.getSibling();
			}
			return (Tag<T>) s;
		}
	}
	
	@Override public int indexOf(Tag<T> sibling) {
		TagSibling s = this.sibling;
		for (int i = 0; i < siblings; i++) {
			if (s == sibling)
				return i;
			s = s.getSibling();
		}
		return -1;
	}
	
	@Override public boolean addSibling(Tag<T> sibling) {
		return addSibling(sibling,silent);
	}
	
	@Override public boolean addSibling(Tag<T> sibling, int index) {
		return addSibling(sibling,index,silent);
	}
	
	public boolean addSibling(Tag<T> sibling, boolean silent) {
		return addSibling(sibling,siblings,silent);
	}
	
	public boolean addSibling(Tag<T> sibling, int index, boolean silent) {
		if (sibling.getSibling() != null)
			return false;
		else if (index == siblings) {
			this.last.setSibling(sibling);
			this.last = sibling;
			sibling.setSibling(this);
		} else if (index < siblings) {
			TagSibling current = this;
			for (int i = 0; i < index; i++)
				current = current.getSibling();
			TagSibling next = current.getSibling();
			current.setSibling(sibling);
			sibling.setSibling(next);
		} else
			return false;
		
		siblings++;
		if (!silent) {
			int[] indicies = {index};
			Object[] values = {sibling};
			TagEvent e = new TagEvent(parent,Action.CHILD_ADDED,indicies,values);
			parent.broadcastEvent(e);
		}
		return true;
	}
	
	@Override public boolean removeSibling(Tag<T> sibling) {
		return removeSibling(sibling,silent);
	}
	
	public boolean removeSibling(Tag<T> sibling, boolean silent) {
		TagSibling current = this, next = current.getSibling();
		int index = 0;
		while (next != this) {
			if (next == sibling) {
				current.setSibling(sibling.getSibling());
				sibling.setSibling(null);
				if (last == sibling)
					last = current;
				siblings--;
				if (!silent) {
					int[] indicies = {index};
					Object[] values = {sibling};
					TagEvent e = new TagEvent(parent,Action.CHILD_REMOVED,indicies,values);
					parent.broadcastEvent(e);
				}
				return true;
			} else {
				index++;
				current = next;
				next = next.getSibling();
			}
		}
		return false;
	}
	
	@Override public boolean removeSibling(int index) {
		return removeSibling(index,silent);
	}
	
	public boolean removeSibling(int index, boolean silent) {
		if (index >= siblings) 
			return false;
		
		TagSibling current = this;
		for (int i = 0; i < index; i++)
			current = current.getSibling();
		TagSibling next = current.getSibling(), succeeder = next.getSibling();
		current.setSibling(succeeder);
		next.setSibling(null);
		if (index == siblings-1)
			last = succeeder; 
		
		siblings--;
		if (!silent) {
			int[] indicies = {index};
			Object[] values = {next};
			TagEvent e = new TagEvent(parent,Action.CHILD_REMOVED,indicies,values);
			parent.broadcastEvent(e);
		}
		return true;
	}
	
	@Override public void clear() {
		clear(silent);
	}
	
	public void clear(boolean silent) {
		int[] indicies = null;
		Object[] values = null;
		TagSibling current = this, next = sibling;
		sibling = this;
		last = this;
		siblings = 0;
		
		if (!silent) {
			int index = 0;
			indicies = new int[siblings];
			values = new Object[siblings];
			while (next != this) {
				current = next;
				next = next.getSibling();
				current.setSibling(null);
				indicies[index] = index;
				values[index] = current;
			}
			parent.broadcastEvent(new TagEvent(parent,Action.CHILD_REMOVED,indicies,values));
		} else {
			// Clear ring without notifying parent
			while (next != this) {
				current = next;
				next = next.getSibling();
				current.setSibling(null);
			}
		}
	}

	@Override public Iterator<Tag<T>> iterator() {
		return new TagIterator();
	}
	
	private final class TagIterator implements Iterator<Tag<T>> {
		
		private TagSibling next = sibling;

		@Override public boolean hasNext() {
			return (next != TagRing.this);
		}

		@SuppressWarnings("unchecked")
		@Override public Tag<T> next() {
			TagSibling current = next;
			next = next.getSibling();
			return (Tag<T>) current;
		}

		@Override public void remove() {
			// Not supported
		}
		
	}

	@Override public boolean hasChildren() {
		return false;
	}

	@Override public TagGeneration<Tag<?>> getChildren() {
		return null;
	}

}
